//
//  auto_test.h
//  GCDBenchmark
//
//  Auto test runner for command-line tracing
//

#ifndef auto_test_h
#define auto_test_h

#import <Foundation/Foundation.h>
#include "basic_case.h"
#include "playback.h"

// Helper function: Parse integer argument with default value
static inline NSInteger parseIntArg(NSArray *arguments, NSString *argName, NSInteger defaultValue) {
    NSUInteger index = [arguments indexOfObject:argName];
    if (index == NSNotFound || index + 1 >= arguments.count) {
        return defaultValue;
    }
    return [arguments[index + 1] integerValue];
}

// Helper function: Parse string argument
static inline NSString* parseStringArg(NSArray *arguments, NSString *argName, NSString *defaultValue) {
    NSUInteger index = [arguments indexOfObject:argName];
    if (index == NSNotFound || index + 1 >= arguments.count) {
        return defaultValue;
    }
    return arguments[index + 1];
}

// Helper function: Parse duration argument with unit suffix (us, ms, etc.)
static inline NSInteger parseDurationArg(NSArray *arguments, NSString *argName, NSInteger defaultValue) {
    NSString *value = parseStringArg(arguments, argName, nil);
    if (value == nil) {
        return defaultValue;
    }

    // Remove unit suffix (us, ms, etc.) and parse as integer
    NSString *cleanValue = [value stringByReplacingOccurrencesOfString:@"us" withString:@""];
    cleanValue = [cleanValue stringByReplacingOccurrencesOfString:@"ms" withString:@""];
    cleanValue = [cleanValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

    NSInteger result = [cleanValue integerValue];
    return (result > 0) ? result : defaultValue;
}

// Helper function: Parse QoS class argument
static inline qos_class_t parseQoSArg(NSArray *arguments, qos_class_t defaultValue) {
    // Look for --qos-class parameter
    NSUInteger index = [arguments indexOfObject:@"--qos-class"];
    if (index == NSNotFound || index + 1 >= arguments.count) {
        return defaultValue;
    }

    NSInteger qosValue = [arguments[index + 1] integerValue];

    // Map integer to QoS class
    // 2 = QOS_CLASS_DEFAULT
    // -1 = QOS_CLASS_UNSPECIFIED
    switch (qosValue) {
        case 2: return QOS_CLASS_DEFAULT;
        case -1: return QOS_CLASS_UNSPECIFIED;
        default: return defaultValue;
    }
}

// Check if running in auto-test mode and execute
// Forward declaration
void runAutoTestWithArguments(NSArray *arguments);

void runAutoTest() {
    NSArray *arguments = [[NSProcessInfo processInfo] arguments];
    runAutoTestWithArguments(arguments);
}

// New function that accepts NSArray - works for both CLI and iOS
void runAutoTestWithArguments(NSArray *arguments) {
    get_cycles_per_us();
    // Check for --auto-test flag
    NSUInteger autoTestIndex = [arguments indexOfObject:@"--auto-test"];
    if (autoTestIndex == NSNotFound || autoTestIndex + 1 >= arguments.count) {
        return;  // Not in auto-test mode
    }

    // Add test start signpost - this will be our reference timestamp
    TRACE_COUNT("test_start", 1);

    NSString *testCase = arguments[autoTestIndex + 1];

    // Parse optional parameters with defaults
    size_t task_type = (size_t)parseIntArg(arguments, @"--task-type", 0);        // 0=CPU-bound, 1=IO, 2=Memory
    size_t task_count = (size_t)parseIntArg(arguments, @"--task-count", 10);
    size_t task_duration = (size_t)parseDurationArg(arguments, @"--task-duration", 1000);  // microseconds
    qos_class_t qos = parseQoSArg(arguments, QOS_CLASS_DEFAULT);

    // Additional parameters for specific tests
    size_t depth = (size_t)parseIntArg(arguments, @"--depth", 5);                // For fibonacci
    size_t buffer_count = (size_t)parseIntArg(arguments, @"--buffer-count", 1);  // For airaw
    size_t slice_count = (size_t)parseIntArg(arguments, @"--slice-count", 512);  // For airaw
    size_t submit_interval = (size_t)parseDurationArg(arguments, @"--submit-interval", 100);  // For queue tests (us)
    size_t max_concurrency = (size_t)parseIntArg(arguments, @"--max-concurrency", 2);   // For concurrent_queue
    // size_t timer_interval = (size_t)parseDurationArg(arguments, @"--timer-interval", 10);   // For periodic (ms) - DEPRECATED
    NSString *playbackFile = parseStringArg(arguments, @"--playback-file", nil);  // For playback

    // Run test on background thread to not block main thread
    @autoreleasepool {
        if ([testCase isEqualToString:@"fork_join"]) {
            execute_fork_join_test(task_type, task_count, task_duration, qos);

        } else if ([testCase isEqualToString:@"fibonacci"]) {
            execute_fibonacci_test(task_type, depth, task_duration, qos);

        } else if ([testCase isEqualToString:@"airaw"]) {
            execute_ariaw_semaphore_test(task_type, buffer_count, slice_count, task_duration, qos);

        } else if ([testCase isEqualToString:@"serial_queue"]) {
            execute_serial_queue_test(task_type, task_count, task_duration, submit_interval, qos);

        } else if ([testCase isEqualToString:@"concurrent_queue"]) {
            execute_concurrent_queue_test(task_type, task_count, task_duration, int(max_concurrency), submit_interval, qos);

        } else if ([testCase isEqualToString:@"periodic"]) {
            execute_periodic_tasks_test(task_type, task_count, task_duration, qos);

        } else if ([testCase isEqualToString:@"playback"]) {
#if TARGET_OS_IOS
            // iOS: Load playback files from main bundle
            NSArray *allResources = [[NSBundle mainBundle] pathsForResourcesOfType:@"playback" inDirectory:nil];

            if (allResources.count > 0) {
                // Use the first available playback file if no specific file provided
                NSString *selectedFile = playbackFile ? [playbackFile lastPathComponent] : [allResources.firstObject lastPathComponent];

                // Find the full path for the selected file
                NSString *fullPath = nil;
                for (NSString *resourcePath in allResources) {
                    if ([[resourcePath lastPathComponent] isEqualToString:selectedFile]) {
                        fullPath = resourcePath;
                        break;
                    }
                }

                if (fullPath) {
                    std::string filepath = std::string([fullPath UTF8String]);
                    run_playback(filepath, 0);
                } else {
                    std::string filepath = std::string([allResources.firstObject UTF8String]);
                    run_playback(filepath, 0);
                }
            }
#else
            // Mac CLI: Load playback files from filesystem
            if (playbackFile) {
                NSString *fullPath = nil;
                // If full path provided, use it directly
                if ([playbackFile hasPrefix:@"/"]) {
                    fullPath = playbackFile;
                } else {
                    // Otherwise, search in PlaybackCases directory
                    NSString *projectDir = [[NSBundle mainBundle] bundlePath];
                    projectDir = [projectDir stringByDeletingLastPathComponent]; // Go up from build products
                    projectDir = [projectDir stringByDeletingLastPathComponent]; // Go up from Debug/
                    NSString *playbackDir = [projectDir stringByAppendingPathComponent:@"PlaybackCases"];
                    fullPath = [playbackDir stringByAppendingPathComponent:playbackFile];
                }

                if (fullPath && [[NSFileManager defaultManager] fileExistsAtPath:fullPath]) {
                    std::string filepath = std::string([fullPath UTF8String]);
                    run_playback(filepath, 0);
                }
            }
#endif
        }
    }

    // Add test end signpost - this will be our reference timestamp
    TRACE_COUNT("test_end", 1);

    // Keep process alive long enough for trace collection
    usleep(200000); // 2 seconds delay - enough time for xctrace to collect
    exit(0);
}

#endif /* auto_test_h */
